//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ENBSeries effect file
// http://enbdev.com
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//THIS IS HLSL FILE FORMAT FOR EXECUTING ADDITIONAL
//POST PROCESSING EFFECTS. MAKE THE COPY BEFORE CHANGING IT!

//WARNING! This is new standart file, compatible with ENBSeries 0.101 or better



uniform sampler2D bgl_RenderedTexture;

void main()
{
   vec4 sum = vec4(0);
   vec2 texcoord = vec2(gl_TexCoord[0]);
   int j;
   int i;

   for( i= -4 ;i < 4; i++)
   {
        for (j = -3; j < 3; j++)
        {
            sum += texture2D(bgl_RenderedTexture, texcoord + vec2(j, i)*0.004) * 0.25;
        }
   }
       if (texture2D(bgl_RenderedTexture, texcoord).r < 0.3)
    {
       gl_FragColor = sum*sum*0.012 + texture2D(bgl_RenderedTexture, texcoord);
    }
    else
    {
        if (texture2D(bgl_RenderedTexture, texcoord).r < 0.5)
        {
            gl_FragColor = sum*sum*0.009 + texture2D(bgl_RenderedTexture, texcoord);
        }
        else
        {
            gl_FragColor = sum*sum*0.0075 + texture2D(bgl_RenderedTexture, texcoord);
        }
    }
}


    height_v[i][j] += ((height_west+ height_east + height_south + height_north)/4 - height[i][j]);
    height_v[i][j] *= damping;
    height[i][j] += height_v[i][j];

#version 330 core
layout (location = 0) out vec4 gPositionDepth;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedoSpec;

in vec2 TexCoords;
in vec3 FragPos;
in vec3 Normal;

const float NEAR = 0.1; // projection matrix's near plane
const float FAR = 50.0f; // projection matrix's far plane
float LinearizeDepth(float depth)
{
    float z = depth * 2.0 - 1.0; // Back to NDC 
    return (2.0 * NEAR * FAR) / (FAR + NEAR - z * (FAR - NEAR));	
}

void main()
{    
    // Store the fragment position vector in the first gbuffer texture
    gPositionDepth.xyz = FragPos;
    // And store linear depth into gPositionDepth's alpha component
    gPositionDepth.a = LinearizeDepth(gl_FragCoord.z); 
    // Also store the per-fragment normals into the gbuffer
    gNormal = normalize(Normal);
    // And the diffuse per-fragment color
    gAlbedoSpec.rgb = vec3(0.95);
}  
glGenTextures(1, &gPositionDepth);
glBindTexture(GL_TEXTURE_2D, gPositionDepth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);  
std::uniform_real_distribution<GLfloat> randomFloats(0.0, 1.0); // random floats between 0.0 - 1.0
std::default_random_engine generator;
std::vector<glm::vec3> ssaoKernel;
for (GLuint i = 0; i < 64; ++i)
{
    glm::vec3 sample(
        randomFloats(generator) * 2.0 - 1.0, 
        randomFloats(generator) * 2.0 - 1.0, 
        randomFloats(generator)
    );
    sample = glm::normalize(sample);
    sample *= randomFloats(generator);
    GLfloat scale = GLfloat(i) / 64.0; 
    ssaoKernel.push_back(sample);  
}
 scale = lerp(0.1f, 1.0f, scale * scale);
   sample *= scale;
   ssaoKernel.push_back(sample);  
}
GLfloat lerp(GLfloat a, GLfloat b, GLfloat f)
{
    return a + f * (b - a);
}  
std::vector<glm::vec3> ssaoNoise;
for (GLuint i = 0; i < 16; i++)
{
    glm::vec3 noise(
        randomFloats(generator) * 2.0 - 1.0, 
        randomFloats(generator) * 2.0 - 1.0, 
        0.0f); 
    ssaoNoise.push_back(noise);
}  
GLuint noiseTexture; 
glGenTextures(1, &noiseTexture);
glBindTexture(GL_TEXTURE_2D, noiseTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);  
GLuint ssaoFBO;
glGenFramebuffers(1, &ssaoFBO);  
glBindFramebuffer(GL_FRAMEBUFFER, ssaoFBO);
GLuint ssaoColorBuffer;

glGenTextures(1, &ssaoColorBuffer);
glBindTexture(GL_TEXTURE_2D, ssaoColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ssaoColorBuffer, 0);  
// geometry pass: render stuff into G-buffer
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
    [...]
glBindFramebuffer(GL_FRAMEBUFFER, 0);  
  
// use G-buffer to render SSAO texture
glBindFramebuffer(GL_FRAMEBUFFER, ssaoFBO);
    glClear(GL_COLOR_BUFFER_BIT);
    shaderSSAO.Use();
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, gPositionDepth);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, gNormal);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, noiseTexture);
    SendKernelSamplesToShader();
    glUniformMatrix4fv(projLocation, 1, GL_FALSE, glm::value_ptr(projection));
    RenderQuad();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
  
// lighting pass: render scene lighting
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shaderLightingPass.Use();
[...]
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, ssaoColorBuffer);
[...]
RenderQuad();  
#version 330 core
out float FragColor;
in vec2 TexCoords;

uniform sampler2D gPositionDepth;
uniform sampler2D gNormal;
uniform sampler2D texNoise;

uniform vec3 samples[64];
uniform mat4 projection;

// tile noise texture over screen based on screen dimensions divided by noise size
const vec2 noiseScale = vec2(800.0/4.0, 600.0/4.0); // screen = 800x600

void main()
{
    [...]
}
vec3 fragPos = texture(gPositionDepth, TexCoords).xyz;
vec3 normal = texture(gNormal, TexCoords).rgb;
vec3 randomVec = texture(texNoise, TexCoords * noiseScale).xyz;  
vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
vec3 bitangent = cross(normal, tangent);
mat3 TBN = mat3(tangent, bitangent, normal);  
float occlusion = 0.0;
for(int i = 0; i < kernelSize; ++i)
{
    // get sample position
    vec3 sample = TBN * samples[i]; // From tangent to view-space
    sample = fragPos + sample * radius; 
    
    [...]
}  
vec4 offset = vec4(sample, 1.0);
offset = projection * offset; // from view to clip-space
offset.xyz /= offset.w; // perspective divide
offset.xyz = offset.xyz * 0.5 + 0.5; // transform to range 0.0 - 1.0  
float sampleDepth = -texture(gPositionDepth, offset.xy).w; 
occlusion += (sampleDepth >= sample.z ? 1.0 : 0.0);
float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth));
occlusion += (sampleDepth >= sample.z ? 1.0 : 0.0) * rangeCheck;
}
occlusion = 1.0 - (occlusion / kernelSize);
FragColor = occlusion;
GLuint ssaoBlurFBO, ssaoColorBufferBlur;
glGenFramebuffers(1, &ssaoBlurFBO);
glBindFramebuffer(GL_FRAMEBUFFER, ssaoBlurFBO);
glGenTextures(1, &ssaoColorBufferBlur);
glBindTexture(GL_TEXTURE_2D, ssaoColorBufferBlur);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ssaoColorBufferBlur, 0);
#version 330 core
in vec2 TexCoords;
out float fragColor;

uniform sampler2D ssaoInput;

void main() {
    vec2 texelSize = 1.0 / vec2(textureSize(ssaoInput, 0));
    float result = 0.0;
    for (int x = -2; x < 2; ++x) 
    {
        for (int y = -2; y < 2; ++y) 
        {
            vec2 offset = vec2(float(x), float(y)) * texelSize;
            result += texture(ssaoInput, TexCoords + offset).r;
        }
    }
    fragColor = result / (4.0 * 4.0);
}
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;

uniform sampler2D gPositionDepth;
uniform sampler2D gNormal;
uniform sampler2D gAlbedo;
uniform sampler2D ssao;

struct Light {
    vec3 Position;
    vec3 Color;
    
    float Linear;
    float Quadratic;
    float Radius;
};
uniform Light light;

void main()
{             
    // Retrieve data from gbuffer
    vec3 FragPos = texture(gPositionDepth, TexCoords).rgb;
    vec3 Normal = texture(gNormal, TexCoords).rgb;
    vec3 Diffuse = texture(gAlbedo, TexCoords).rgb;
    float AmbientOcclusion = texture(ssao, TexCoords).r;
    
    // Blinn-Phong (in view-space)
    vec3 ambient = vec3(0.3 * AmbientOcclusion); // Here we add occlusion factor
    vec3 lighting  = ambient; 
    vec3 viewDir  = normalize(-FragPos); // Viewpos is (0.0.0) in view-space
    // Diffuse
    vec3 lightDir = normalize(light.Position - FragPos);
    vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * light.Color;
    // Specular
    vec3 halfwayDir = normalize(lightDir + viewDir);  
    float spec = pow(max(dot(Normal, halfwayDir), 0.0), 8.0);
    vec3 specular = light.Color * spec;
    // Attenuation
    float dist = length(light.Position - FragPos);
    float attenuation = 1.0 / (1.0 + light.Linear * dist + light.Quadratic * dist * dist);
    diffuse  *= attenuation;
    specular *= attenuation;
    lighting += diffuse + specular;

    FragColor = vec4(lighting, 1.0);
}
occlusion = 1.0 - (occlusion / kernelSize);       
FragColor = pow(occlusion, power);
uniform sampler2D tex;
uniform sampler2D tex2;
uniform mat4 PrevMatrix;
varying vec2 vTexCoord;
 
void main(void)
{
vec4 color = texture2D(tex, vTexCoord);
vec4 zOverW = texture2D(tex2, vTexCoord);
// H is the viewport position at this pixel in the range -1 to 1.
vec4 H = vec4(vTexCoord.x * 2 - 1, (1 - vTexCoord.y) * 2 - 1, zOverW.g, 1);
// Transform by the view-projection inverse.
vec4 D = gl_ModelViewProjectionMatrixInverse * H;
// Divide by w to get the world position.
vec4 worldPos = D / vec4(D.w);
 
// Current viewport position
vec4 currentPos = H;
// Use the world position, and transform by the previous view-projection matrix.
vec4 previousPos = PrevMatrix * worldpos;
// Convert to nonhomogeneous points [-1,1] by dividing by w.
previousPos = previousPos / vec4(previousPos.w);
// Use this frame's position and last frame's to compute the pixel velocity.
vec2 velocity = vec2(currentPos.xy - previousPos.xy)/2.0;
//velocity = (velocity + 1.0 ) / 2.0;
 
gl_FragColor = vec4(velocity.x, velocity.y, color.g, 1.0);
}

varying vec3 ReflectDir;
 
void main (void)
{
 
	//Enable texture coordinates
	gl_TexCoord[0] = gl_MultiTexCoord0;
	gl_TexCoord[1] = gl_MultiTexCoord1;
	gl_TexCoord[2] = gl_MultiTexCoord2;
	gl_TexCoord[3] = gl_MultiTexCoord3;
 
	//Cubemap
	gl_Position    = ftransform();
	vec3 normal    = normalize(gl_NormalMatrix*gl_Normal);
	vec4 pos       = gl_ModelViewMatrix*gl_Vertex;
	vec3 eyeDir    = pos.xyz;
	ReflectDir     = reflect(eyeDir,normal);
}
uniform samplerCube cubemap;
varying vec3  ReflectDir;
 
void main (void)
{
	vec4 color;
 
	//Cubemap
	color=vec4(textureCube(cubemap,ReflectDir));
	gl_FragColor=clamp(color,0.0,1.0);
 uniform sampler2D bgl_RenderedTexture;
uniform sampler2D bgl_DepthTexture;
uniform float bgl_RenderedTextureWidth;
uniform float bgl_RenderedTextureHeight;

#define PI  3.14159265

float width = bgl_RenderedTextureWidth; //texture width
float height = bgl_RenderedTextureHeight; //texture height

vec2 texel = vec2(1.0/width,1.0/height);

//uniform variables from external script

uniform float focalDepth;  //focal distance value in meters, but you may use autofocus option below
uniform float focalLength; //focal length in mm
uniform float fstop; //f-stop value
uniform bool showFocus; //show debug focus point and focal range (red = focal point, green = focal range)

/* 
make sure that these two values are the same for your camera, otherwise distances will be wrong.
*/

float znear = 0.1; //camera clipping start
float zfar = 100.0; //camera clipping end

//------------------------------------------
//user variables

int samples = 3; //samples on the first ring
int rings = 3; //ring count

bool manualdof = false; //manual dof calculation
float ndofstart = 1.0; //near dof blur start
float ndofdist = 2.0; //near dof blur falloff distance
float fdofstart = 1.0; //far dof blur start
float fdofdist = 3.0; //far dof blur falloff distance

float CoC = 0.03;//circle of confusion size in mm (35mm film = 0.03mm)

bool vignetting = true; //use optical lens vignetting?
float vignout = 1.3; //vignetting outer border
float vignin = 0.0; //vignetting inner border
float vignfade = 22.0; //f-stops till vignete fades

bool autofocus = false; //use autofocus in shader? disable if you use external focalDepth value
vec2 focus = vec2(0.5,0.5); // autofocus point on screen (0.0,0.0 - left lower corner, 1.0,1.0 - upper right)
float maxblur = 1.0; //clamp value of max blur (0.0 = no blur,1.0 default)

float threshold = 0.5; //highlight threshold;
float gain = 2.0; //highlight gain;

float bias = 0.5; //bokeh edge bias
float fringe = 0.7; //bokeh chromatic aberration/fringing

bool noise = true; //use noise instead of pattern for sample dithering
float namount = 0.0001; //dither amount

bool depthblur = false; //blur the depth buffer?
float dbsize = 1.25; //depthblursize

/*
next part is experimental
not looking good with small sample and ring count
looks okay starting from samples = 4, rings = 4
*/

bool pentagon = false; //use pentagon as bokeh shape?
float feather = 0.4; //pentagon shape feather

//------------------------------------------


float penta(vec2 coords) //pentagonal shape
{
	float scale = float(rings) - 1.3;
	vec4  HS0 = vec4( 1.0,         0.0,         0.0,  1.0);
	vec4  HS1 = vec4( 0.309016994, 0.951056516, 0.0,  1.0);
	vec4  HS2 = vec4(-0.809016994, 0.587785252, 0.0,  1.0);
	vec4  HS3 = vec4(-0.809016994,-0.587785252, 0.0,  1.0);
	vec4  HS4 = vec4( 0.309016994,-0.951056516, 0.0,  1.0);
	vec4  HS5 = vec4( 0.0        ,0.0         , 1.0,  1.0);
	
	vec4  one = vec4( 1.0 );
	
	vec4 P = vec4((coords),vec2(scale, scale)); 
	
	vec4 dist = vec4(0.0);
	float inorout = -4.0;
	
	dist.x = dot( P, HS0 );
	dist.y = dot( P, HS1 );
	dist.z = dot( P, HS2 );
	dist.w = dot( P, HS3 );
	
	dist = smoothstep( -feather, feather, dist );
	
	inorout += dot( dist, one );
	
	dist.x = dot( P, HS4 );
	dist.y = HS5.w - abs( P.z );
	
	dist = smoothstep( -feather, feather, dist );
	inorout += dist.x;
	
	return clamp( inorout, 0.0, 1.0 );
}

float bdepth(vec2 coords) //blurring depth
{
	float d = 0.0;
	float kernel[9];
	vec2 offset[9];
	
	vec2 wh = vec2(texel.x, texel.y) * dbsize;
	
	offset[0] = vec2(-wh.x,-wh.y);
	offset[1] = vec2( 0.0, -wh.y);
	offset[2] = vec2( wh.x -wh.y);
	
	offset[3] = vec2(-wh.x,  0.0);
	offset[4] = vec2( 0.0,   0.0);
	offset[5] = vec2( wh.x,  0.0);
	
	offset[6] = vec2(-wh.x, wh.y);
	offset[7] = vec2( 0.0,  wh.y);
	offset[8] = vec2( wh.x, wh.y);
	
	kernel[0] = 1.0/16.0;   kernel[1] = 2.0/16.0;   kernel[2] = 1.0/16.0;
	kernel[3] = 2.0/16.0;   kernel[4] = 4.0/16.0;   kernel[5] = 2.0/16.0;
	kernel[6] = 1.0/16.0;   kernel[7] = 2.0/16.0;   kernel[8] = 1.0/16.0;
	
	
	for( int i=0; i<9; i++ )
	{
		float tmp = texture2D(bgl_DepthTexture, coords + offset[i]).r;
		d += tmp * kernel[i];
	}
	
	return d;
}


vec3 color(vec2 coords,float blur) //processing the sample
{
	vec3 col = vec3(0.0);
	
	col.r = texture2D(bgl_RenderedTexture,coords + vec2(0.0,1.0)*texel*fringe*blur).r;
	col.g = texture2D(bgl_RenderedTexture,coords + vec2(-0.866,-0.5)*texel*fringe*blur).g;
	col.b = texture2D(bgl_RenderedTexture,coords + vec2(0.866,-0.5)*texel*fringe*blur).b;
	
	vec3 lumcoeff = vec3(0.299,0.587,0.114);
	float lum = dot(col.rgb, lumcoeff);
	float thresh = max((lum-threshold)*gain, 0.0);
	return col+mix(vec3(0.0),col,thresh*blur);
}

vec2 rand(vec2 coord) //generating noise/pattern texture for dithering
{
	float noiseX = ((fract(1.0-coord.s*(width/2.0))*0.25)+(fract(coord.t*(height/2.0))*0.75))*2.0-1.0;
	float noiseY = ((fract(1.0-coord.s*(width/2.0))*0.75)+(fract(coord.t*(height/2.0))*0.25))*2.0-1.0;
	
	if (noise)
	{
		noiseX = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233))) * 43758.5453),0.0,1.0)*2.0-1.0;
		noiseY = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233)*2.0)) * 43758.5453),0.0,1.0)*2.0-1.0;
	}
	return vec2(noiseX,noiseY);
}

vec3 debugFocus(vec3 col, float blur, float depth)
{
	float edge = 0.002*depth; //distance based edge smoothing
	float m = clamp(smoothstep(0.0,edge,blur),0.0,1.0);
	float e = clamp(smoothstep(1.0-edge,1.0,blur),0.0,1.0);
	
	col = mix(col,vec3(1.0,0.5,0.0),(1.0-m)*0.6);
	col = mix(col,vec3(0.0,0.5,1.0),((1.0-e)-(1.0-m))*0.2);

	return col;
}

float linearize(float depth)
{
	return -zfar * znear / (depth * (zfar - znear) - zfar);
}

float vignette()
{
	float dist = distance(gl_TexCoord[3].xy, vec2(0.5,0.5));
	dist = smoothstep(vignout+(fstop/vignfade), vignin+(fstop/vignfade), dist);
	return clamp(dist,0.0,1.0);
}

void main() 
{
	//scene depth calculation
	
	float depth = linearize(texture2D(bgl_DepthTexture,gl_TexCoord[0].xy).x);
	
	if (depthblur)
	{
		depth = linearize(bdepth(gl_TexCoord[0].xy));
	}
	
	//focal plane calculation
	
	float fDepth = focalDepth;
	
	if (autofocus)
	{
		fDepth = linearize(texture2D(bgl_DepthTexture,focus).x);
	}
	
	//dof blur factor calculation
	
	float blur = 0.0;
	
	if (manualdof)
	{    
		float a = depth-fDepth; //focal plane
		float b = (a-fdofstart)/fdofdist; //far DoF
		float c = (-a-ndofstart)/ndofdist; //near Dof
		blur = (a>0.0)?b:c;
	}
	
	else
	{
		float f = focalLength; //focal length in mm
		float d = fDepth*1000.0; //focal plane in mm
		float o = depth*1000.0; //depth in mm
		
		float a = (o*f)/(o-f); 
		float b = (d*f)/(d-f); 
		float c = (d-f)/(d*fstop*CoC); 
		
		blur = abs(a-b)*c;
	}
	
	blur = clamp(blur,0.0,1.0);
	
	// calculation of pattern for ditering
	
	vec2 noise = rand(gl_TexCoord[0].xy)*namount*blur;
	
	// getting blur x and y step factor
	
	float w = (1.0/width)*blur*maxblur+noise.x;
	float h = (1.0/height)*blur*maxblur+noise.y;
	
	// calculation of final color
	
	vec3 col = vec3(0.0);
	
	if(blur < 0.05) //some optimization thingy
	{
		col = texture2D(bgl_RenderedTexture, gl_TexCoord[0].xy).rgb;
	}
	
	else
	{
		col = texture2D(bgl_RenderedTexture, gl_TexCoord[0].xy).rgb;
		float s = 1.0;
		int ringsamples;
		
		for (int i = 1; i <= rings; i += 1)
		{   
			ringsamples = i * samples;
			
			for (int j = 0 ; j < ringsamples ; j += 1)   
			{
				float step = PI*2.0 / float(ringsamples);
				float pw = (cos(float(j)*step)*float(i));
				float ph = (sin(float(j)*step)*float(i));
				float p = 1.0;
				if (pentagon)
				{ 
					p = penta(vec2(pw,ph));
				}
				col += color(gl_TexCoord[0].xy + vec2(pw*w,ph*h),blur)*mix(1.0,(float(i))/(float(rings)),bias)*p;  
				s += 1.0*mix(1.0,(float(i))/(float(rings)),bias)*p;   
			}
		}
		col /= s; //divide by sample count
	}
	
	if (showFocus)
	{
		col = debugFocus(col, blur, depth);
	}
	
	if (vignetting)
	{
		col *= vignette();
	}
	
	gl_FragColor.rgb = col;
	gl_FragColor.a = 1.0;
}
pixelColor= Ambient + (Diffuse + Specular) * Shadow
Ambient = ambientMaterial * ambientLight
Diffuse = diffuseMaterial * diffuseLight * lamberFactor
lamberFactor = max (dot (lightVec, normal), 0.0) 
Specular = specularMaterial * specularLight * speculatCoef
speculatCoef = pow (max (dot (halfVec, normal), 0.0), shininess)
[	Normal.x 	Normal.y 	Normal.z	]
[	BiNormal.x	BiNormal.y	BiNormal.z	]
[	Tangent.x	Tangent.y	Tangent.z	]
for each faces in model
	{
		generate the face's normal via cross product
		fore each vertex in the face, add the normal vector
	}
	
	for each vertices in model
		normalize normal vector
generateNormalAndTangent(float3 v1, float3 v2, text2 st1, text2 st2)
	{
		float3 normal = v1.crossProduct(v2);
		
		float coef = 1/ (st1.u * st2.v - st2.u * st1.v);
		float3 tangent;

		tangent.x = coef * ((v1.x * st2.v)  + (v2.x * -st1.v));
		tangent.y = coef * ((v1.y * st2.v)  + (v2.y * -st1.v));
		tangent.z = coef * ((v1.z * st2.v)  + (v2.z * -st1.v));
		
		float3 binormal = normal.crossProduct(tangent);
	}
// The vertex VBO is updated every frame for animation purpose
	glBindBufferARB(GL_ARRAY_BUFFER_ARB,vboVertexId);
	glVertexPointer (3, GL_FLOAT, 0, 0);

	// Same as vertex VBO: updated every frames
	glBindBufferARB(GL_ARRAY_BUFFER_ARB,vboNormalId);
	glNormalPointer(GL_FLOAT,0, 0);

	// VBO, created and populated once, texture coordinate never change
	glBindBufferARB(GL_ARRAY_BUFFER_ARB,vboTexturId);
	glTexCoordPointer(2,GL_FLOAT,0,0);

	// Tangent generated previously, no need to pass the binormal, a cross product will generate it
	glVertexAttribPointerARB(tangentLoc, 3, GL_FLOAT,GL_FALSE, 0, tangentArraySkinPointer);

	// VBO, created and populated once, elements to draw never change 
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB,vboElementsId);
	glDrawElements (GL_TRIANGLES, meshes[i].facesCount * 3 , GL_UNSIGNED_INT, 0);

	
	glActiveTextureARB(GL_TEXTURE0);
	glBindTexture(diffuseTextureId);
	glUniform1iARB(diffuseTextureUniform,0);
	
	glActiveTextureARB(GL_TEXTURE1);
	glBindTexture(normalTextureId);
	glUniform1iARB(normalTextureUniform,0);
	
	glActiveTextureARB(GL_TEXTURE2);
	glBindTexture(heightTextureId);
	glUniform1iARB(heightTextureUniform,0);
attribute vec3 tangent;
	varying vec3 lightVec;
	varying vec3 halfVec;
	varying vec3 eyeVec;
	

  void main()
  {

	gl_TexCoord[0] =  gl_MultiTexCoord0;
	
	// Building the matrix Eye Space -> Tangent Space
	vec3 n = normalize (gl_NormalMatrix * gl_Normal);
	vec3 t = normalize (gl_NormalMatrix * tangent);
	vec3 b = cross (n, t);
	
	vec3 vertexPosition = vec3(gl_ModelViewMatrix *  gl_Vertex);
	vec3 lightDir = normalize(gl_LightSource[0].position.xyz - vertexPosition);
		
		
	// transform light and half angle vectors by tangent basis
	vec3 v;
	v.x = dot (lightDir, t);
	v.y = dot (lightDir, b);
	v.z = dot (lightDir, n);
	lightVec = normalize (v);
	
	  
	v.x = dot (vertexPosition, t);
	v.y = dot (vertexPosition, b);
	v.z = dot (vertexPosition, n);
	eyeVec = normalize (v);
	
	
	vertexPosition = normalize(vertexPosition);
	
	/* Normalize the halfVector to pass it to the fragment shader */

	// No need to divide by two, the result is normalized anyway.
	// vec3 halfVector = normalize((vertexPosition + lightDir) / 2.0); 
	vec3 halfVector = normalize(vertexPosition + lightDir);
	v.x = dot (halfVector, t);
	v.y = dot (halfVector, b);
	v.z = dot (halfVector, n);

	// No need to normalize, t,b,n and halfVector are normal vectors.
	//normalize (v);
	halfVec = v ; 
	  
	  
	gl_Position = ftransform();

  }

	
uniform sampler2D diffuseTexture;
	uniform sampler2D normalTexture;
	
	// New bumpmapping
	varying vec3 lightVec;
	varying vec3 halfVec;
	varying vec3 eyeVec;


  void main()
  { 

	// lookup normal from normal map, move from [0,1] to  [-1, 1] range, normalize
	vec3 normal = 2.0 * texture2D (normalTexture, gl_TexCoord[0].st).rgb - 1.0;
	normal = normalize (normal);
	
	// compute diffuse lighting
	float lamberFactor= max (dot (lightVec, normal), 0.0) ;
	vec4 diffuseMaterial = 0.0;
	vec4 diffuseLight  = 0.0;
	
	// compute specular lighting
	vec4 specularMaterial ;
	vec4 specularLight ;
	float shininess ;
  
	// compute ambient
	vec4 ambientLight = gl_LightSource[0].ambient;	
	
	if (lamberFactor > 0.0)
	{
		diffuseMaterial = texture2D (diffuseTexture, gl_TexCoord[0].st);
		diffuseLight  = gl_LightSource[0].diffuse;
		
		// In doom3, specular value comes from a texture 
		specularMaterial =  vec4(1.0)  ;
		specularLight = gl_LightSource[0].specular;
		shininess = pow (max (dot (halfVec, normal), 0.0), 2.0)  ;
		 
		gl_FragColor =	diffuseMaterial * diffuseLight * lamberFactor ;
		gl_FragColor +=	specularMaterial * specularLight * shininess ;				
	
	}
	
	gl_FragColor +=	ambientLight;
	
  }			
// size of the shadow map
GLuint shadowMapSize = 1024;

// create the shadow map
GLint shadowMap;
glGenTextures(1, &shadowMap);
glBindTexture(GL_TEXTURE_2D, shadowMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, shadowMapSize, shadowMapSize,
                  0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

// GL_CLAMP_TO_EDGE setups the shadow map in such a way that
// fragments for which the shadow map is undefined
// will get values from closest edges of the shadow map
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

// comparison mode of the shadow map
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);

////////////////////////////////////////////////////

// create framebuffer
GLint shadowFramebuffer;
glGenFramebuffers(1, &shadowFramebuffer);

// attach the shadow map to framebuffer
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shadowFramebuffer);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                          GL_TEXTURE_2D, shadowMap, 0);

// depth is stored in z-buffer to which the shadow map is attached,
// so there is no need for any color buffers
glDrawBuffer(GL_NONE); 
// activate offset for polygons
glEnable(GL_POLYGON_OFFSET_FILL);
// offset by two units equal to smallest value of change in the shadow map
// and offset by two units depending on the slope of the polygon
glPolygonOffset(2.0f, 2.0f); 
// size of the viewport should be equal to size of the shadow map
glViewport(0, 0, shadowMapSize, shadowMapSize);
// set framebuffer for first rendering pass
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shadowFramebuffer);
// clear the shadow map with default value
glClearDepth(1.0);
glClear(GL_DEPTH_BUFFER_BIT);

// set the shader, set the matrices and render the objects
glUseProgram(pass1Shader->id);
glUniformMatrix4fv(pass1Shader->shadowViewMat, 1, GL_FALSE, shadowViewMat);
glUniformMatrix4fv(pass1Shader->shadowProjMat, 1, GL_FALSE, shadowProjectionMat);
foreach(auto obj : renderables)
{
   glUniformMatrix4fv(pass1Shader->objModelMat, 1, GL_FALSE, obj->modelMat());
   obj->draw();
}

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDisable(GL_POLYGON_OFFSET_FILL);
glViewport(0, 0, windowWidth, windowHeight);
#version 330

// attributes
layout(location = 0) in vec3 i_position; // xyz - position

// matrices
uniform mat4 u_modelMat;
uniform mat4 u_shadowViewMat;
uniform mat4 u_shadowProjMat;

void main(void)
{
   // transform to screen space
   // shader will save z/w value to z-buffer
   gl_Position = u_shadowProjMat * u_shadowViewMat * u_modelMat * vec4(i_position, 1);
} 
#version 330

// color to framebuffer
layout (location = 0) out vec4 resultingColor;

void main(void)
{
   // fragment shader by default is obligated to output color
   resultingColor = vec4(0, 0, 0, 1);
} 
glm::mat4 shadowViewMat = ... // light's view matrix
glm::mat4 shadowProjMat = ... // light's projection matrix

// offset matrix that maps from [-1, 1] to [0, 1] range
glm::mat4 offsetMat = glm::mat4(
      glm::vec4(0.5f, 0.0f, 0.0f, 0.0f),
      glm::vec4(0.0f, 0.5f, 0.0f, 0.0f),
      glm::vec4(0.0f, 0.0f, 0.5f, 0.0f),
      glm::vec4(0.5f, 0.5f, 0.5f, 1.0f)
   );

// combination of matrices into shadowMat
shadowMat = offsetMat * shadowProjMat * shadowViewMat; 
#version 330

// attributes
layout(location = 0) in vec3 i_position; // xyz - position

// matrices
uniform mat4 u_modelMat;
uniform mat4 u_viewProjectionMat;
uniform mat4 u_shadowMat;

// texture coordinates to the fragment shader
// for access to the shadow map
out vec4 o_shadowCoord;

void main(void)
{
   // position of the vertex in the scene
   vec4 worldPos = u_modelMat * vec4(i_position, 1);

   // screen space position of the vertex from the light source's point of view
   // plus it is mapped from range [-1, 1]
   // to [0, 1] by shadowMat in order to be used
   // as texture coordinates
   o_shadowCoord = u_shadowMat * worldPos;

   // screen space position of the vertex from camera's point of view
   gl_Position = u_viewProjectionMat * worldPos;
} 
#version 330

// the shadow map from first rendering pass
layout(location = 0) uniform sampler2DShadow u_shadowMap;

// texture coordinates for access to the shadow map
in vec4 o_shadowCoord;

// color to the framebuffer
layout(location = 0) out vec4 resultingColor;

void main(void)
{
   // sampling from the shadow map
   // textureProj() divides o_shadowCoord.xyz by o_shadowCoord.w
   // then o_shadowCoord.xy are used as 2D texture coords to sample the shadow map
   // o_shadowCoord.z is automatically compared to the value from the shadow map
   float shadowFactor = textureProj(u_shadowMap, o_shadowCoord);

   // save write or black color to the framebuffer (light/shadow)
   resultingColor.rgb = vec3(shadowFactor);
   resultingColor.a = 1;
} 
// function that encodes float as RBGA
vec4 encode(float val)
{
   vec4 o;
   val *= 255;
   o.r = floor(val);
   val = (val - o.r) * 255;
   o.g = floor(val);
   val = (val - o.g) * 255;
   o.b = floor(val);
   val = (val - o.b) * 255;
   o.a = floor(val);
   return o;
}

// function that decodes RGBA into float
float decode(vec4 val)
{
   return val.r/255.0
      + val.g/(255.0*255.0)
      + val.b/(255.0*255.0*255.0)
      + val.a/(255.0*255.0*255.0*255.0);
} 

			


		VertexShader = compile vs_3_0 VS_PostProcess();
		PixelShader  = compile ps_3_0 PS_Process();

		DitherEnable=FALSE;
		ZEnable=FALSE;
		CullMode=NONE;
		ALPHATESTENABLE=FALSE;
		SEPARATEALPHABLENDENABLE=FALSE;
		AlphaBlendEnable=FALSE;
		StencilEnable=FALSE;
		FogEnable=FALSE;
		SRGBWRITEENABLE=FALSE;
	}
}
